package com.cloudsiteservice8004.util;

public class SnowflakeIdGenerator {
    // 起始时间戳（2023-01-01 00:00:00），用于计算时间差
    private final long twepoch = 1672531200000L;

    // 机器ID所占的位数
    private final long workerIdBits = 5L;
    // 数据中心ID所占的位数
    private final long datacenterIdBits = 5L;

    // 支持的最大机器ID，结果是31（2^5 - 1）
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // 支持的最大数据中心ID，结果是31（2^5 - 1）
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    // 序列在ID中占的位数
    private final long sequenceBits = 12L;

    // 机器ID向左移12位
    private final long workerIdShift = sequenceBits;
    // 数据中心ID向左移17位（12 + 5）
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    // 时间戳向左移22位（12 + 5 + 5）
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    // 生成序列的掩码，这里为4095（0b111111111111 = 2^12 - 1）
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    // 工作机器ID（0~31）
    private long workerId;
    // 数据中心ID（0~31）
    private long datacenterId;
    // 毫秒内序列（0~4095）
    private long sequence = 0L;
    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     *
     * @param workerId     工作机器ID（0~31）
     * @param datacenterId 数据中心ID（0~31）
     */
    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId 超出范围 [0, %d]", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenterId 超出范围 [0, %d]", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 生成下一个唯一的ID
     *
     * @return 唯一ID
     */
    public synchronized Long nextId() {
        long currentTimestamp = currentTimeMillis();

        // 如果当前时间小于上一次生成ID的时间戳，说明系统时钟回拨，此时抛出异常
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("系统时钟回拨，当前时间 %d 小于上一次生成ID的时间 %d", currentTimestamp, lastTimestamp));
        }

        // 如果当前时间与上一次生成ID的时间戳相同，则在毫秒内生成序列号
        if (currentTimestamp == lastTimestamp) {
            // 序列号自增
            sequence = (sequence + 1) & sequenceMask;
            // 如果序列号溢出（即超过4095），则等待下一毫秒
            if (sequence == 0) {
                currentTimestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            // 如果当前时间与上一次生成ID的时间戳不同，则序列号重置为0
            sequence = 0L;
        }

        // 更新上一次生成ID的时间戳
        lastTimestamp = currentTimestamp;

        // 组合各部分生成最终的ID
        return ((currentTimestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

    /**
     * 等待下一毫秒
     *
     * @param lastTimestamp 上一次生成ID的时间戳
     * @return 当前时间戳
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 获取当前时间戳（毫秒）
     *
     * @return 当前时间戳
     */
    private long currentTimeMillis() {
        return System.currentTimeMillis();
    }
}
